package com.liuche.pictureserver.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liuche.pictureserver.aop.annotation.Mask;
import com.liuche.pictureserver.common.exception.BizException;
import com.liuche.pictureserver.common.exception.ErrorCode;
import com.liuche.pictureserver.common.exception.ThrowUtils;
import com.liuche.pictureserver.common.resp.PageData;
import com.liuche.pictureserver.enums.ProcessEnum;
import com.liuche.pictureserver.enums.ReviewStatusEnum;
import com.liuche.pictureserver.mapper.PictureMapper;
import com.liuche.pictureserver.pojo.domain.PictureDO;
import com.liuche.pictureserver.pojo.domain.UserDO;
import com.liuche.pictureserver.pojo.dto.PictureSaveDTO;
import com.liuche.pictureserver.pojo.req.picture.PicturePageReqVO;
import com.liuche.pictureserver.pojo.req.picture.PictureReviewReqVO;
import com.liuche.pictureserver.pojo.req.picture.PictureSaveReqVO;
import com.liuche.pictureserver.pojo.resp.picture.PictureRespVO;
import com.liuche.pictureserver.pojo.resp.picture.PictureSaveReviewRespVO;
import com.liuche.pictureserver.pojo.resp.picture.PictureSimpleInfoVO;
import com.liuche.pictureserver.pojo.resp.user.UserInfoRespVO;
import com.liuche.pictureserver.process.ProcessContext;
import com.liuche.pictureserver.process.ProcessController;
import com.liuche.pictureserver.service.IPictureService;
import com.liuche.pictureserver.service.IUserService;
import com.liuche.pictureserver.utils.BeanUtil;
import com.liuche.pictureserver.utils.UserHolder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author liuche
 * @description 针对表【picture(图片信息)】的数据库操作Service实现
 * @createDate 2025-01-14 23:06:58
 */
@Slf4j
@Service
public class PictureServiceImpl extends ServiceImpl<PictureMapper, PictureDO>
        implements IPictureService {
    private static final List<String> ALLOW_CONTENT_TYPES = Arrays.asList("image/jpeg", "image/jpg", "image/png", "image/webp");
    private static final long FIVE_LIMIT = 5 * 1024 * 1024L;

    @Resource
    private ProcessController processController;
    @Resource
    private IUserService userService;

    @Override
    public PictureSimpleInfoVO upload(PictureSaveDTO saveDTO) {
        log.info("PictureService upload saveInfo[{}]", JSONUtil.toJsonStr(saveDTO));
        // 拼接参数
        ProcessContext<PictureSaveDTO> processContext = new ProcessContext<>(saveDTO);
        // 执行链
        ProcessContext<PictureSaveDTO> runProcess = processController.runProcess(ProcessEnum.PICTURE_UPLOAD, processContext);
        if (runProcess.isBreakFlag()) {
            throw new BizException(runProcess.getErrorCode());
        }
        return BeanUtil.toBean(runProcess.getModel(), PictureSimpleInfoVO.class);
    }

    @Mask
    @Override
    public PageData<PictureRespVO> pageQuery(PicturePageReqVO req) {
        log.info("PictureService pageQuery queryInfo[{}]", JSONUtil.toJsonStr(req));
        // 构造分页参数
        // 普通人看不到非审核通过的图片信息
        if (ObjectUtils.isEmpty(UserHolder.getUserInfo()) || "user".equals(UserHolder.getUserRole())) {
            req.setReviewStatus(ReviewStatusEnum.REVIEW_PASS.getCode());
        }
        Page<PictureDO> page = new Page<>(req.getCurrent(), req.getPageSize());
        IPage<PictureDO> pageResult = this.baseMapper.queryPage(page, req);
        if (CollUtil.isEmpty(pageResult.getRecords())) {
            return PageData.create(Collections.emptyList(), page.getTotal(), page.getCurrent(), (int) page.getSize());
        }
        return PageData.create(BeanUtil.toBean(pageResult.getRecords(), PictureRespVO.class), pageResult.getTotal(), pageResult.getCurrent(), (int) pageResult.getSize());
    }

    @Mask
    @Override
    public PictureRespVO queryById(Long id) {
        log.info("PictureService queryById id[{}]", id);
        PictureRespVO respVO = BeanUtil.toBean(this.baseMapper.selectById(id), PictureRespVO.class);
        // 查询用户信息
        UserDO user = userService.queryById(respVO.getUserId());
        UserInfoRespVO infoRespVO = BeanUtil.toBean(user, UserInfoRespVO.class);
        respVO.setUserInfo(infoRespVO);
        return respVO;
    }

    @Override
    public PictureSaveReviewRespVO save(PictureSaveReqVO req) {
        log.info("PictureService save req[{}]", JSONUtil.toJsonStr(req));
        saveReqConvert(req);
        PictureDO pictureDO = BeanUtil.toBean(req, PictureDO.class);
        reviewImages(pictureDO);
        if (ObjectUtils.isNotEmpty(pictureDO.getId())) {
            pictureDO.setEditTime(LocalDateTime.now());
            boolean updateFlag = this.updateById(pictureDO);
            if (!updateFlag) {
                throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR);
            }
            return PictureSaveReviewRespVO.builder().id(pictureDO.getId()).reviewStatus(pictureDO.getReviewStatus()).build();
        }
        pictureDO.setUserId(UserHolder.getUserId());
        boolean saveFlag = this.save(pictureDO);
        if (!saveFlag) {
            throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR);
        }
        return PictureSaveReviewRespVO.builder().id(pictureDO.getId()).reviewStatus(pictureDO.getReviewStatus()).build();
    }

    /**
     * 审核图片信息
     *
     * @param pictureDO 图片信息
     */
    private void reviewImages(PictureDO pictureDO) {
        // 判断是不是admin
        String userRole = UserHolder.getUserRole();
        if (StringUtils.equals(userRole, "admin")) {
            pictureDO.setReviewStatus(ReviewStatusEnum.REVIEW_PASS.getCode());
            pictureDO.setReviewMessage("管理员添加，无需审核");
            pictureDO.setReviewerId(UserHolder.getUserId());
            pictureDO.setReviewTime(LocalDateTime.now());
            return;
        }
        pictureDO.setReviewStatus(ReviewStatusEnum.REVIEW_PENDING.getCode());
    }


    @Override
    public PictureSimpleInfoVO saveByUrl(String fileUrl) {
        log.info("PictureService saveByUrl url[{}]", fileUrl);
        // 一、校验url信息
        try {
            // 1. 验证 URL 格式
            // 验证是否是合法的 URL
            new URL(fileUrl);
        } catch (MalformedURLException e) {
            throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR, "文件地址格式不正确");
        }
        // 2. 校验 URL 协议
        ThrowUtils.throwIf(!(fileUrl.startsWith("http://") || fileUrl.startsWith("https://")),
                ErrorCode.UPLOAD_PICTURE_ERROR, "仅支持 HTTP 或 HTTPS 协议的文件地址");

        // 3. 发送 HEAD 请求以验证文件是否存在
        String contentType;
        try (HttpResponse response = HttpUtil.createRequest(Method.HEAD, fileUrl).timeout(2000).execute()) {
            // 未正常返回，无需执行其他判断
            if (response.getStatus() != HttpStatus.HTTP_OK) {
                throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR, "无法获取图片信息");
            }
            // 4. 校验文件类型
            contentType = response.header("Content-Type");
            if (StringUtils.isNotBlank(contentType)) {
                // 允许的图片类型
                ThrowUtils.throwIf(!ALLOW_CONTENT_TYPES.contains(contentType.toLowerCase()),
                        ErrorCode.UPLOAD_PICTURE_ERROR, "文件类型错误");
            } else {
                log.info("picture content type can not get, url [{}]", fileUrl);
                throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR, "解析不到文件类型");
            }
            // 5. 校验文件大小
            String contentLengthStr = response.header("Content-Length");
            if (StringUtils.isNotBlank(contentLengthStr)) {
                long contentLength = Long.parseLong(contentLengthStr);
                // 限制文件大小为 5MB
                ThrowUtils.throwIf(contentLength > FIVE_LIMIT, ErrorCode.UPLOAD_PICTURE_ERROR, "文件大小不能超过 5M");
            }
        } catch (Exception e) {
            log.error("请求失败: ", e);
            throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR, "请求失败，或超时，请稍后再试");
        }

        // 二、封装成multipart文件调用upload方法
        File file;
        PictureSimpleInfoVO res;
        try {
            file = File.createTempFile("temp", null);
            HttpUtil.downloadFile(fileUrl, file, 2000);
        } catch (Exception e) {
            log.error("==> PictureService saveByUrl file download error", e);
            throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR, "文件下载失败");
        }

        MultipartFile multipartFile;
        try {
            multipartFile = com.liuche.pictureserver.utils.FileUtil.toMultipartFile(file, contentType);
        } catch (IOException e) {
            throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR, "文件转换失败");
        }

        // 上传解析文件
        try {
            PictureSaveDTO dto = PictureSaveDTO.builder().file(multipartFile).build();
            res = upload(dto);
        } catch (Exception e) {
            log.error("==> PictureService saveByUrl upload error", e);
            if (!FileUtil.del(file)) {
                log.error("==> PictureService saveByUrl file delete error");
            }

            throw new BizException(ErrorCode.UPLOAD_PICTURE_ERROR, "文件上传失败");
        }

        if (!FileUtil.del(file)) {
            log.error("==> PictureService saveByUrl file delete error");
        }
        return res;
    }

    @Override
    public Boolean review(PictureReviewReqVO req) {
        PictureDO pictureDO = new PictureDO();
        pictureDO.setId(req.getId());
        pictureDO.setReviewerId(UserHolder.getUserId());
        pictureDO.setReviewTime(LocalDateTime.now());
        pictureDO.setReviewStatus(req.getReviewStatus());
        pictureDO.setReviewMessage(req.getReviewMessage());

        return this.updateById(pictureDO);
    }


    /**
     * 参数类型转换
     *
     * @param req 结果
     */
    private void saveReqConvert(PictureSaveReqVO req) {
        req.setTags(JSONUtil.toJsonStr(req.getTagList()));
    }

}




